package com.xjgz.aio.socket.buffer;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public final class BufferPagePool {

    private static final ScheduledThreadPoolExecutor BUFFER_POOL_CLEAN = new ScheduledThreadPoolExecutor(1, r -> {
        Thread thread = new Thread(r, "BufferPoolClean");
        thread.setDaemon(true);
        return thread;
    });

    public static final BufferPagePool DEFAULT_BUFFER_PAGE_POOL = new BufferPagePool(1, false);

    private final AtomicInteger cursor = new AtomicInteger(0);

    private BufferPage[] bufferPages;

    private boolean enabled = true;

    private final ScheduledFuture<?> future;

    private BufferPagePool(final int pageNum, boolean isDirect) {
        if (isDirect && DirectBufferCleaner.isDirectSupported()) {
            isDirect = false;
            System.err.println("The current version of JDK does not support applying for Direct ByteBuffer");
            System.err.println("Automatically downgraded to Heap ByteBuffer");
        }

        bufferPages = new BufferPage[pageNum];
        for (int i = 0; i < pageNum; i++) {
            bufferPages[i] = new BufferPage(isDirect);
        }

        if (pageNum > 0) {
            future = BUFFER_POOL_CLEAN.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    if (enabled) {
                        for (BufferPage bufferPage : bufferPages) {
                            bufferPage.tryClean();
                        }
                    } else {
                        if (bufferPages != null) {
                            for (BufferPage bufferPage : bufferPages) {
                                bufferPage.release();
                            }
                            bufferPages = null;
                        }
                        future.cancel(false);
                    }
                }
            }, 500, 1000, TimeUnit.MILLISECONDS);
        } else {
            future = null;
        }
    }

    public VirtualBuffer allocateSequentially(final int size) {
        return bufferPages[(cursor.getAndIncrement() & Integer.MAX_VALUE) % bufferPages.length].allocate(size);
    }

    public VirtualBuffer allocateByThreadId(final int size) {
        return bufferPages[(int) ((Thread.currentThread().getId()) % bufferPages.length)].allocate(size);
    }

    public void release() {
        enabled = false;
    }

    @Override
    public String toString() {
        StringBuilder logger = new StringBuilder();
        for (BufferPage bufferPage : bufferPages) {
            logger.append("\n").append(bufferPage.toString());
        }
        return logger.toString();
    }
}
